#include "Component.h"
#include "ParsingErrorException.h"
#include "Scene.h"

ConcreteElementLoader<Component> *Component::loader = new ConcreteElementLoader<Component>();

map<string, void (*)(TiXmlElement *, Component *)> tag_parser;

void parseTransformation(TiXmlElement *elem, Component *c) {
	TiXmlElement *child = elem->FirstChildElement();

	if( child == NULL ) {
		c->transformation = NULL;
		return;
	}
	if( !strcmp(child->Value(), "transformationref") ) {
		string transref_id = Scene::testStringAttribute(child, "id");
		c->transformation = Scene::transformations[transref_id];
	}
	else {
		Transformation *t = new Transformation();
		
		t->parseTransformations(child);
		c->transformation = t;
	}
}

void parseMaterials(TiXmlElement *elem, Component *c) { // TODO handle keys
	bool inherit = false; // TODO handle inherit material
	try {
		string key = Scene::testStringAttribute(elem, "key");
		c->key = key;
		c->material = NULL;
	} catch(ParsingErrorException *e) {
		TiXmlElement *child = elem->FirstChildElement();
		string mat_id = Scene::testStringAttribute(child, "id");
		if( mat_id != "inherit" ) {
			Scene::testIdAttribute(child, &Scene::materials);
			c->material = Scene::materials[mat_id];
		}
	}
	
	for( TiXmlElement *child = elem->FirstChildElement(); child != NULL; child = child->NextSiblingElement() ) {
		string mat_id = Scene::testStringAttribute(child, "id");
	
		if( inherit && mat_id == "inherit" ) {
			throw new ParsingErrorException("more than one inherit material declared.");
		}
		else if( !inherit && mat_id == "inherit" ) {
			inherit = true;
		}
		else {
			Scene::testIdAttribute(child, &Scene::materials);
			c->materials.push_back(Scene::materials[mat_id]);
		}
	}
}

void parseTexture(TiXmlElement *elem, Component *c) {
	string tex_id = Scene::testStringAttribute(elem, "id");

	if( tex_id == "none" || tex_id == "inherit" ) {
		c->texture = NULL;
		c->inherit_texture = true;
		return;
	}

	c->inherit_texture = false;
	Scene::testIdAttribute(elem, &Scene::textures);

	c->texture = Scene::textures[tex_id];
}

void parseChildren(TiXmlElement *elem, Component *c) {

	for( TiXmlElement *child = elem->FirstChildElement(); child != NULL; child = child->NextSiblingElement() ) {
		string ref_id = Scene::testStringAttribute(child, "id");

		if( !strcmp(child->Value(), "primitiveref") ) {
			Scene::testIdAttribute(child, &Scene::primitives);
			c->childs.push_back(Scene::primitives[ref_id]);
		}
		else if( !strcmp(child->Value(), "componentref") ) {
			if( Scene::components.count(ref_id) == 0 ) {
				c->missing_childs.push_back(ref_id);
			}
			else
				c->childs.push_back(Scene::components[ref_id]);
		}
		else
			throw new ParsingErrorException("unknown tag.");
	}
}

void initComponentTagParsers() {
	if( tag_parser.empty() ) {
		tag_parser["transformation"] = parseTransformation;
		tag_parser["materials"] = parseMaterials;
		tag_parser["texture"] = parseTexture;
		tag_parser["children"] = parseChildren;
	}
}

Component::Component(TiXmlElement *elem) {
	initComponentTagParsers();

	this->id = elem->Attribute("id");
	this->isPrimitive = false;

	for( TiXmlElement *child = elem->FirstChildElement(); child != NULL; child = child->NextSiblingElement() ) {
		if( tag_parser.count(child->Value()) == 0 )
			throw new ParsingErrorException((string) child->Value() + ": invalid tag");
		try {
			tag_parser[child->Value()](child, this);
		} catch(ParsingErrorException *e) {
			e->concatTag(child->Value());
			throw e;
		}
	}
}

void Component::displayElement() {
	// TODO push of materials, textures.

	glPushMatrix();
	if( transformation != NULL )
		glMultMatrixf( transformation->matrix );
	
	enableObjectProperties();
	
	for(vector<Object *>::iterator it = childs.begin(); it != childs.end(); ++it) {
		enableObjectProperties();
		(*it)->displayElement();
	}
	
	disableObjectProperties();

	glPopMatrix();
	// TODO pop of materials, textures.
}